package cn.seaboot.commons.file;

import cn.seaboot.commons.core.Converter;
import cn.seaboot.commons.reflect.FieldAccess;
import cn.seaboot.commons.reflect.ObjectField;
import org.dom4j.Attribute;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 包含一些常见的 xml 数据解析
 *
 * @author Mr.css
 * @version 2022-08-24 16:57
 */
public class SaxUtils {

    /**
     * 将文件转换为 ListMap 格式
     * <p>
     * 通常用于简单的数据报文解析，IO 流中只包含格式简单的数据。
     *
     * <pre><code>
     * <root>
     * <person><name>ming</name><age>33</age></person>
     * <person><name>ming</name><age>33</age></person>
     * </root>
     * </code></pre>
     *
     * @param is 输入流
     * @return 数据
     */
    public static List<Map<String, Object>> toListMapByElement(InputStream is) throws DocumentException {
        SAXReader reader = new SAXReader();
        Document document = reader.read(is);

        // 获取根节点
        Element root = document.getRootElement();
        return SaxUtils.toListMapByElement(root);
    }

    /**
     * 将文件转换为 ListMap 格式
     * <p>
     * 通常用于简单的数据报文解析，IO 流中只包含格式简单的数据。
     *
     * <pre><code>
     * <root>
     * <person name = "min" age = "33">
     * <person name = "min" age = "33">
     * </root>
     * </code></pre>
     *
     * @param is 输入流
     * @return 数据
     */
    public static List<Map<String, Object>> toListMapByAttribute(InputStream is) throws DocumentException {
        SAXReader reader = new SAXReader();
        Document document = reader.read(is);

        // 获取根节点
        Element root = document.getRootElement();
        return SaxUtils.toListMapByAttribute(root);
    }

    /**
     * 将某个节点下的所有子节点转换为 ListMap 格式，数据取自节点的 attribute 属性
     *
     * <pre><code>
     * <list>
     * <person name = "min" age = "33">
     * <person name = "min" age = "33">
     * </list>
     * </code></pre>
     *
     * @param element 节点
     * @return 数据
     */
    public static List<Map<String, Object>> toListMapByAttribute(Element element) {
        List<Element> elements = element.elements();
        List<Map<String, Object>> res = new ArrayList<>(elements.size());
        for (Element ele : elements) {
            res.add(toMapByAttribute(ele));
        }
        return res;
    }


    /**
     * 将某个节点下的所有子节点转换为 ListMap 格式，数据取自节点的 text 属性
     *
     * <pre><code>
     * <list>
     * <person><name>ming</name><age>33</age></person>
     * <person><name>ming</name><age>33</age></person>
     * </list>
     * </code></pre>
     *
     * @param element 节点
     * @return 数据
     */
    public static List<Map<String, Object>> toListMapByElement(Element element) {
        List<Element> elements = element.elements();
        List<Map<String, Object>> res = new ArrayList<>(elements.size());
        for (Element ele : elements) {
            res.add(toMapByElement(ele));
        }
        return res;
    }


    /**
     * 将节点的属性转换为 Map
     *
     * <pre><code>
     * <person name = "min" age = "33">
     * </code></pre>
     *
     * @param element 节点
     * @return ret
     */
    public static Map<String, Object> toMapByAttribute(Element element) {
        Map<String, Object> ret = new HashMap<>();
        List<Attribute> attributes = element.attributes();
        for (Attribute attribute : attributes) {
            ret.put(attribute.getName(), attribute.getValue());
        }
        return ret;
    }


    /**
     * 将某个节点的所有子节点转换为 Map
     *
     * <pre><code>
     * <person><name>ming</name><age>33</age></person>
     * </code></pre>
     *
     * @param element 节点
     * @return ret
     */
    public static Map<String, Object> toMapByElement(Element element) {
        Map<String, Object> ret = new HashMap<>();
        List<Element> list = element.elements();
        for (Element ele : list) {
            ret.put(ele.getName(), ele.getText());
        }
        return ret;
    }


    /**
     * 将某个节点的所有子节点转换为 Map
     *
     * <pre><code>
     * <property name="name">ming</person>
     * <property name="age">33</person>
     * </code></pre>
     *
     * @param element 节点
     * @return ret
     */
    public static Map<String, Object> toMapByProperty(Element element) {
        Map<String, Object> ret = new HashMap<>();
        List<Element> list = element.elements();
        for (Element ele : list) {
            ret.put(ele.attribute("name").getValue(), ele.getText());
        }
        return ret;
    }

    /**
     * 将节点的属性转换为 Object
     *
     * <pre><code>
     * <person name = "min" age = "33">
     * </code></pre>
     *
     * @param element 节点
     * @return ret
     */
    public static <T> T toObjectByAttribute(Element element, Class<T> clazz) {
        FieldAccess access = FieldAccess.create(clazz);
        T ret = access.newInstance();
        List<Attribute> attributes = element.attributes();
        for (Attribute attribute : attributes) {
            ObjectField field = access.getField(attribute.getName());
            field.setValue(ret, Converter.convert(attribute.getValue(), field.getType()));
        }
        return ret;
    }

    /**
     * 将某个节点的所有子节点转换为 Object
     *
     * <pre><code>
     * <person><name>ming</name><age>33</age></person>
     * </code></pre>
     *
     * @param element 节点
     * @return ret
     */
    public static <T> T toObjectByElement(Element element, Class<T> clazz) {
        FieldAccess access = FieldAccess.create(clazz);
        T ret = access.newInstance();
        List<Element> list = element.elements();
        for (Element ele : list) {
            ObjectField field = access.getField(ele.getName());
            field.setValue(ret, Converter.convert(ele.getText(), field.getType()));
        }
        return ret;
    }
}
